home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / mewin10s.zip / MSWDISP.C < prev    next >
C/C++ Source or Header  |  1992-04-12  |  19KB  |  570 lines

  1. /* The routines in this file provide display support under
  2.    the Microsoft Windows environment on an IBM-PC or compatible
  3.    computer.
  4.  
  5.    Must be compiled with Borland C++ 2.0.
  6.  
  7.    It should not be compiled if the WINDOW_MSWIN symbol is not set */
  8.  
  9. /* The functions in this module are mostly concerned with the mapping
  10.    between character cells and client coordinates. */
  11.  
  12. #include    "estruct.h"
  13. #include    "elang.h"
  14. #include    <stdio.h>
  15. #include    <time.h>
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18.  
  19. #include    "mswin.h"
  20.  
  21. #if     COLOR
  22. /* palette table. It is initialized for the default colors, but can be
  23.    changed by the spal function */
  24. static COLORREF EmacsPalette [16] = {
  25.     0x00000000,     /* black */
  26.     0x00000080,     /* red */
  27.     0x00008000,     /* green */
  28.     0x0000FFFF,     /* yellow */
  29.     0x00800000,     /* blue */
  30.     0x00800080,     /* magenta */
  31.     0x00808000,     /* cyan */
  32.     0x00C0C0C0,     /* grey (light) */
  33.     0x00808080,     /* gray (dark) */
  34.     0x000000FF,     /* light red */
  35.     0x0000FF00,     /* light green */
  36.     0x0080FFFF,     /* light yellow*/
  37.     0x00FF0000,     /* light  blue */
  38.     0x00800080,     /* light magenta*/
  39.     0x00FFFF00,     /* light cyan */
  40.     0x00FFFFFF      /* white */
  41. };
  42. #endif
  43.  
  44. static HWND hCaretWnd = 0;          /* window where the caret belongs */
  45. static int  CaretVisible = 0;       /* the caret should be visible if not 0 */
  46. static int  CaretCol, CaretRow;     /* caret position */
  47. /* Text Metrics values (from the CellMetrics structure) are used as follows:
  48.  
  49.     -------------------------------- Client area upper boundary
  50.     |          ^
  51.     |       OffsetY
  52.     |          v
  53.     |          ^
  54.     |      HalfLeadingY
  55.     |          v
  56.     |       ---------- ----------
  57.     |<---->|          |          |  ^
  58.   OffsetX  |   cell   |   cell   |  |    . . .
  59.     |      |    0,0   |    1,0   | SizeY
  60.     |      |          |          |  |
  61.     |      |          |          |  v
  62.     |       ---------- ----------
  63.     |                         ^
  64.     |       <- SizeX->     LeadingY = 2 * HalfLeadingY
  65.     |                         v
  66.     |       ---------- ----------
  67.     |      |          |          |
  68.     |      |   cell   |   cell   |
  69.     |      |    0,1   |    1,1   |
  70.     |      |          |          |
  71.     |
  72.     |                ...
  73.     |
  74.    Client area left boundary
  75. */
  76.  
  77. /* BuildCellMetrics:   fills a CellMetrics structure from a font description */
  78. /* ================                                                          */
  79.  
  80. void far pascal BuildCellMetrics (CellMetrics *cm, HFONT hFont)
  81. {
  82.     HDC     hDC;
  83.     TEXTMETRIC Metrics;
  84.     HANDLE  hPrevFont;
  85.  
  86.     hDC = GetDC (hFrameWnd);
  87.     hPrevFont = SelectFont (hDC, hFont);
  88.     GetTextMetrics (hDC, &Metrics);
  89.     SelectObject (hDC, hPrevFont);
  90.     ReleaseDC (hFrameWnd, hDC);
  91.     cm->SizeX = Metrics.tmAveCharWidth;
  92.     cm->SizeY = Metrics.tmHeight;
  93.     cm->HalfLeadingY = Metrics.tmExternalLeading / 2;
  94.     cm->OffsetX = cm->SizeX / 4;
  95.     if ((cm->OffsetY = (cm->SizeY / 8) - cm->HalfLeadingY) < 0) {
  96.     cm->OffsetY = 0;
  97.     /* for a reasonable upper boundary separation, we want
  98.        (SizeY / 8) <= OffsetY + HalfLeadingY */
  99.     }
  100.     cm->LeadingY = 2 * cm->HalfLeadingY;
  101.     cm->MLHeight = cm->SizeY + cm->LeadingY + (2 * cm->OffsetY) +
  102.                    GetSystemMetrics(SM_CYBORDER);
  103. } /* BuildCellMetrics */
  104.  
  105. /* InvalidateCells: marks character cells for repaint */
  106. /* ===============                                    */
  107.  
  108. void far pascal InvalidateCells (HWND hWnd, int leftcol, int toprow,
  109.                                  int rightcol, int bottomrow)
  110. {
  111.     RECT    Rect;           /* used for Cell and Client coordinates */
  112.     RECT    ClientRect;     /* MDI window client area */
  113.     POINT   MaxBottomRight; /* Cell coordinates of bottom right of MDI
  114.                    window */
  115.  
  116.     GetClientRect (hWnd, &ClientRect);
  117.     ClientToCell (hWnd, *(POINT *)&ClientRect.right, &MaxBottomRight);
  118.  
  119.     Rect.left = leftcol;
  120.     Rect.top = toprow;
  121.     CellToClient (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
  122.     if (leftcol == 0) Rect.left = 0;
  123.     if (toprow == 0) Rect.top = 0;
  124.  
  125.     Rect.right = rightcol + 1;
  126.     Rect.bottom = bottomrow + 1;
  127.     CellToClient (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
  128.     if (rightcol + 1 >= MaxBottomRight.x) Rect.right = ClientRect.right;
  129.     if (bottomrow + 1 >= MaxBottomRight.y) Rect.bottom = ClientRect.bottom;
  130.  
  131.     InvalidateRect (hWnd, &Rect, FALSE);
  132.         /* the background does not need erasing since Paint will
  133.        completely fill the invalid rectangle */
  134. } /* InvalidateCells */
  135.  
  136. /* MinimumClientSize:  computes the minimum client area size */
  137. /* =================                                         */
  138.  
  139. void far pascal MinimumClientSize (HWND hWnd, int NCols, int NRows,
  140.                    int *Width, int *Height)
  141.  
  142. /* The values pointed by Height and Width are set to the smallest value
  143.    that allows displaying NRows rows and NCols columns. A NULL pointer
  144.    disables the computing of the associated value */
  145. {
  146.     if (Height) *Height = (2 * EmacsCM.OffsetY) +
  147.                           (NRows * (EmacsCM.SizeY + EmacsCM.LeadingY));
  148.     if (Width) *Width = (2 * EmacsCM.OffsetX) + (NCols * EmacsCM.SizeX);
  149. } /* MinimumClientSize */
  150.  
  151. /* DisplayableRows: returns the number of rows displayable in the client area */
  152. /* ===============                                                            */
  153.  
  154. int far pascal DisplayableRows (HWND hWnd, int Height, CellMetrics *cm)
  155.  
  156. /* Heigh is the hypothetic heigh of the client area. If this parameter
  157.    is 0, the real client area is measured. If it is negative, the
  158.    maximal client area (maximized child in a maximized frame) is used.
  159.    */
  160. {
  161.     RECT    Rect;
  162.     int     x;
  163.  
  164.     if (Height == 0) GetClientRect (hWnd, &Rect);
  165.     else {
  166.     if (Height > 0) {
  167.         Rect.bottom = Height;
  168.     }
  169.     else {
  170.         Rect.bottom = GetSystemMetrics (SM_CYFULLSCREEN) -
  171.                           (GetSystemMetrics (SM_CYMENU) + cm->MLHeight);
  172.     }
  173.     }
  174.     x = (Rect.bottom - (2 * cm->OffsetY)) / (cm->SizeY + cm->LeadingY);
  175.     if (x < 0) return 0;
  176.     return x;
  177. } /* DisplayableRows */
  178.  
  179. /* DisplayableColumns:  returns the number of columns displayable in the client area */
  180. /* ==================                                                                */
  181.  
  182. int far pascal DisplayableColumns (HWND hWnd, int Width, CellMetrics *cm)
  183.  
  184. /* Width is the hypothetic width of the client area. If this parameter
  185.    is 0, the real client area is measured. If it is negative, the
  186.    maximal client area (maximized child in a maximized frame) is used.
  187.    */
  188. {
  189.     RECT    Rect;
  190.     int     x;
  191.  
  192.     if (Width== 0) GetClientRect (hWnd, &Rect);
  193.     else {
  194.     if (Width > 0) {
  195.         Rect.right = Width;
  196.     }
  197.     else {
  198.         Rect.right = GetSystemMetrics (SM_CXFULLSCREEN);
  199.     }
  200.     }
  201.     x = (Rect.right - (2 * cm->OffsetX)) / cm->SizeX;
  202.     if (x < 0) return 0;
  203.     return x;
  204. } /* DisplayableColumns */
  205.  
  206. /* UpdateEmacsCaretPos: position the caret according to CaretCol/CaretRow */
  207. /* ===================                                                    */
  208.  
  209. static void pascal near UpdateEmacsCaretPos (void)
  210. {
  211.     POINT   pt;
  212.  
  213.     pt.x = CaretCol;
  214.     pt.y = CaretRow;
  215.     CellToClient (hCaretWnd, pt, &pt);
  216.     SetCaretPos (pt.x, pt.y + EmacsCM.HalfLeadingY);
  217. } /* UpdateEmacsCaretPos */
  218.  
  219. /* EmacsCaret:  Creates or destroys the caret */
  220. /* ==========                                 */
  221.  
  222. void far pascal EmacsCaret (BOOL Show)
  223.  
  224. /* the Show parameter is TRUE if the caret should be created and FALSE
  225.    if it should be destroyed */
  226. {
  227.     if (Show) {
  228.         if (hCaretWnd == 0) return;
  229.     if (hFrameWnd == GetActiveWindow ()) {
  230.         if (!IsWindow (hCaretWnd)) {
  231.             /* this may happen in some transient cases when closing
  232.            down a screen */
  233.             hCaretWnd = 0;
  234.             return;
  235.         }
  236.             CreateCaret (hCaretWnd, NULL,
  237.              hCaretWnd == hFrameWnd ?
  238.                        GetSystemMetrics (SM_CXBORDER) :
  239. #if BLOCKCARET
  240.                       EmacsCM.SizeX,
  241. #else
  242.                       EmacsCM.SizeX / 4,
  243. #endif
  244.                          EmacsCM.SizeY);
  245.             UpdateEmacsCaretPos ();
  246.             if (CaretVisible && !IsIconic (hCaretWnd)) ShowCaret (hCaretWnd);
  247.         }
  248.     }
  249.     else {
  250.     /* destroy the caret */
  251.     DestroyCaret ();
  252.     }
  253. } /* EmacsCaret */
  254.  
  255. /* MoveEmacsCaret:  updates the caret position */
  256. /* ==============                              */
  257.  
  258. void far pascal MoveEmacsCaret (HWND hWnd, int col, int row)
  259. {
  260.     CaretCol = col;
  261.     CaretRow = row;
  262.     if (hWnd != hCaretWnd) {
  263.     hCaretWnd = hWnd;
  264.         EmacsCaret (TRUE);
  265.     }
  266.     else {
  267.     hCaretWnd = hWnd;
  268.     UpdateEmacsCaretPos ();
  269.     }
  270. } /* MoveEmacsCaret */
  271.  
  272. /* ShowEmacsCaret:  shows or hides the caret used by emacs */
  273. /* ==============                                          */
  274.  
  275. void far pascal ShowEmacsCaret (BOOL Show)
  276.  
  277. /* this function is used to make the caret visible only when waiting for
  278.    user input */
  279. {
  280.     if (Show) {
  281.         if (!CaretVisible) {
  282.             if (!IsIconic (hCaretWnd)) ShowCaret (hCaretWnd);
  283.         }
  284.     ++CaretVisible;
  285.     }
  286.     else {
  287.     --CaretVisible;
  288.         if (!CaretVisible) {
  289.             HideCaret (NULL);
  290.         }
  291.     }
  292. } /* ShowEmacsCaret */
  293.  
  294. /* InMessageLine:   non-zero if caret currently in the message line */
  295. /* =============                                                    */
  296.  
  297. BOOL far pascal InMessageLine (void)
  298. {
  299.     return (hCaretWnd == hFrameWnd);
  300. } /* InMessageLine */
  301.  
  302. /* CellToClient:    converts character cell coordinates into client coordinates */
  303. /* ============                                                                 */
  304.  
  305. void far pascal CellToClient (HWND hWnd, POINT Cell, LPPOINT Client)
  306.  
  307. /* The resulting Client coordinates indicate the upper left pixel one
  308.    HalfLeadingY above the character cell */
  309. {
  310.     Client->x = (Cell.x * EmacsCM.SizeX) + EmacsCM.OffsetX;
  311.     if (hWnd == hFrameWnd ) {
  312.         RECT    Rect;
  313.  
  314.         GetClientRect (hWnd, &Rect);
  315.         Client->y = (Rect.bottom - EmacsCM.MLHeight) + EmacsCM.OffsetY +
  316.                     GetSystemMetrics (SM_CYBORDER);
  317.     }
  318.     else Client->y = (Cell.y * (EmacsCM.SizeY + EmacsCM.LeadingY)) +
  319.                      EmacsCM.OffsetY;
  320. } /* CellToClient */
  321.  
  322. /* ClientToCell:    converts client coordinates into character cell coordinates */
  323. /* ============                                                                 */
  324.  
  325. void far pascal ClientToCell (HWND hWnd, POINT Client, LPPOINT Cell)
  326.  
  327. /* The area associated with a Cell is the character cell itself, plus
  328.    the HalfLeadingY-high areas above and under the cell */
  329. {
  330.     int     MaxCol, MaxRow;
  331.     
  332.     if (hWnd == hFrameWnd) {    /* message line case */
  333.         MaxCol = MLSIZE - 1;
  334.     MaxRow = 0;
  335.     }
  336.     else {                      /* screen case */
  337.         register SCREEN *sp;
  338.  
  339.         sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  340.         MaxCol = sp->s_ncol - 1;
  341.         MaxRow = sp->s_nrow - 1;
  342.     }
  343.     if ((Cell->x = (Client.x - EmacsCM.OffsetX) /
  344.                    EmacsCM.SizeX) < 0) Cell->x = 0;
  345.     else Cell->x = min (Cell->x, MaxCol);
  346.     if ((Cell->y = (Client.y - EmacsCM.OffsetY) /
  347.                    (EmacsCM.SizeY + EmacsCM.LeadingY)) < 0) Cell->y = 0;
  348.     else Cell->y = min (Cell->y, MaxRow);
  349. } /* ClientToCell */
  350.  
  351. /* GetMinMaxInfo:  processes the WM_GETMINMAXINFO message for a screen */
  352. /* =============                                                       */
  353.  
  354. void far pascal GetMinMaxInfo (HWND hWnd, LPPOINT rgpt)
  355. {
  356.     if (InternalRequest) return;    /* none of our business */
  357.  
  358.     if (notquiescent) {
  359.     /* forbid all size changes */
  360.     RECT    Rect;
  361.  
  362.     GetWindowRect (hWnd, &Rect);
  363.     rgpt[1].x = Rect.right - Rect.left;
  364.     rgpt[1].y = Rect.bottom - Rect.top;
  365.     rgpt[2] = *(POINT*)&Rect.left;
  366.     rgpt[3] = rgpt[1];
  367.     rgpt[4] = rgpt[1];
  368.     }
  369.     else {                  /* compute minimum tracking size */
  370.         int     X, Y;
  371.  
  372.         /* minimum displayed text width = 3 */
  373.         /* minimum displayed text  height = 10 */
  374.         MinimumClientSize (hWnd, term.t_margin, 2, &X, &Y);
  375.         rgpt[3].x = X + (2 * GetSystemMetrics (SM_CXFRAME));
  376.         rgpt[3].y = Y + GetSystemMetrics (SM_CYCAPTION) +
  377.                     (2 * GetSystemMetrics (SM_CYFRAME));
  378.     }
  379. } /* GetMinMaxInfo */
  380.  
  381. /* ScrReSize:    processes the WM_SIZE message */
  382. /* =========                                   */
  383.  
  384. BOOL far pascal ScrReSize (HWND hWnd, WORD wParam, WORD cx, WORD cy)
  385.  
  386. /* returns TRUE only if real resizing performed */
  387. {
  388.     BOOL    ChgWidth, ChgHeight;
  389.  
  390.     if ((wParam != SIZENORMAL) && (wParam != SIZEFULLSCREEN)) {
  391.         return FALSE;
  392.     }
  393.     ChgWidth = (cx != GetWindowWord (hWnd, GWW_SCRCX));
  394.     ChgHeight = (cy != GetWindowWord (hWnd, GWW_SCRCY));
  395.     if (!ChgWidth && !ChgHeight) return FALSE;
  396.  
  397.     SetWindowWord (hWnd, GWW_SCRCX, cx);
  398.     SetWindowWord (hWnd, GWW_SCRCY, cy);
  399.     if (!InternalRequest) {
  400.         SCREEN  *TopScreen;
  401.  
  402.         InternalRequest = TRUE;
  403.         TopScreen = first_screen;
  404.     select_screen ((SCREEN *)GetWindowLong (hWnd, GWL_SCRPTR), FALSE);
  405.     if (ChgWidth) {
  406.             newwidth (TRUE, DisplayableColumns (hWnd, cx, &EmacsCM));
  407.         }
  408.     if (ChgHeight) {
  409.         newsize (TRUE, DisplayableRows (hWnd, cy, &EmacsCM));
  410.     }
  411.     select_screen (TopScreen, FALSE);
  412.     update (FALSE);
  413.     InternalRequest = FALSE;
  414.     }
  415.     return TRUE;
  416. } /* ScrReSize */
  417.  
  418. /* ScrPaint:   processes WM_PAINT messages for emacs screens */
  419. /* ========                                                  */
  420.  
  421. void far pascal ScrPaint (HWND hWnd)
  422. {
  423.     SCREEN  *sp;
  424.     PAINTSTRUCT ps;
  425.     HANDLE  hPrevFont;
  426.     RECT    Rect;
  427.     int     Row, BottomRow;
  428.     int     Col;
  429.     int     Length;
  430.  
  431.     /* note that if the WM_PAINT is not passed through the message loop
  432.        (as would hapen with use of UpdateWindow), it is possible to
  433.        attempt repainting while defferupdate is TRUE which means update
  434.        has been deffered */
  435.        
  436.     BeginPaint (hWnd, &ps);
  437.     sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  438.     
  439.     /*-calculate the row/col loop control variables and normalize the
  440.        coordinates of the first line's rectangle into Rect */
  441.     CopyRect (&Rect, &ps.rcPaint);
  442.     ClientToCell (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
  443.     Col = Rect.left;
  444.     Row = Rect.top;
  445.     --Rect.right;       /* in rectangle conventions, */
  446.     --Rect.bottom;      /* the lower/right border is excluded */
  447.     ClientToCell (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
  448.     ++Rect.right;
  449.     Length = Rect.right - Col;
  450.     BottomRow = Rect.bottom;
  451.     Rect.bottom = Rect.top + 1;
  452.     CellToClient (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
  453.     CellToClient (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
  454.     /* Rect now contains a bounding rectangle for the 1st row. This will
  455.        be used to paint the row's background and will be moved down one
  456.        row's height at each iteration of the painting loop */
  457.  
  458.     /*-loop from top to bottom, writing one line at a time */
  459.     hPrevFont = SelectFont (ps.hdc, hEmacsFont);
  460.     do {
  461.     VIDEO   *vp;
  462.     COLORREF FColor, BColor;
  463.     RECT    BoundingRect;
  464.  
  465.     vp = sp->s_physical[Row];
  466.     if (!(vp->v_flag & VFNEW)) {    /* valid contents */
  467. #if     COLOR
  468.         if (ColorDisplay) {
  469.             FColor = EmacsPalette[vp->v_flag & VFREV ?
  470.                                       vp->v_bcolor : vp->v_fcolor];
  471.             BColor = EmacsPalette[vp->v_flag & VFREV ?
  472.                                       vp->v_fcolor : vp->v_bcolor];
  473.             }
  474.             else {  /* monochrome display */
  475. #else
  476.             {
  477. #endif
  478.         if (vp->v_flag & VFREV) {
  479.             FColor = GetSysColor (COLOR_HIGHLIGHTTEXT);
  480.             BColor = GetSysColor (COLOR_HIGHLIGHT);
  481.         }
  482.         else {
  483.             FColor = GetSysColor (COLOR_WINDOWTEXT);
  484.             BColor = GetSysColor (COLOR_WINDOW);
  485.         }
  486.         }
  487.         SetBkColor (ps.hdc, BColor);
  488.         SetTextColor (ps.hdc, FColor);
  489.         CopyRect (&BoundingRect, &ps.rcPaint);
  490.         if (Row > 0) BoundingRect.top = Rect.top;
  491.         if (Row < BottomRow) BoundingRect.bottom = Rect.bottom;
  492.         ExtTextOut (ps.hdc,
  493.                 Rect.left, Rect.top + EmacsCM.HalfLeadingY,
  494.             ETO_OPAQUE, &BoundingRect,
  495.             &vp->v_text[Col],
  496.             Length, NULL);
  497.     }
  498.     Rect.top = Rect.bottom;
  499.     Rect.bottom += EmacsCM.SizeY + EmacsCM.LeadingY;
  500.     } while (++Row <= BottomRow);
  501.  
  502.     SelectObject (ps.hdc, hPrevFont);
  503. EndScrPaint:
  504.     EndPaint (hWnd, &ps);
  505. } /* ScrPaint */
  506.  
  507. /* MLPaint: processes WM_PAINT messages for the Message Line */
  508. /* =======                                                   */
  509.  
  510. void far pascal MLPaint (void)
  511. {
  512.     PAINTSTRUCT ps;
  513.     HANDLE  hPrev, hPen;
  514.     RECT    Rect;
  515.     POINT   Client;
  516.     
  517.     BeginPaint (hFrameWnd, &ps);
  518.     
  519.     /*-draw the text portion targetted for repaint */
  520.     hPrev = SelectFont (ps.hdc, hEmacsFont);
  521.     ClientToCell (hFrameWnd, *(POINT*)&ps.rcPaint.left, (POINT*)&Rect.left);
  522.     ClientToCell (hFrameWnd, *(POINT*)&ps.rcPaint.right, (POINT*)&Rect.right);
  523.     CellToClient (hFrameWnd, *(POINT*)&Rect.left, &Client);
  524.     ExtTextOut (ps.hdc,
  525.                 Client.x, Client.y + EmacsCM.HalfLeadingY,
  526.                 ETO_OPAQUE, &ps.rcPaint,
  527.                 &MLBuf[Rect.left],
  528.                 Rect.right - Rect.left + 1, NULL);
  529.     SelectObject (ps.hdc, hPrev);
  530.     
  531.     /*-draw the separation line at top of message line */
  532.     hPen = CreatePen (PS_SOLID, GetSystemMetrics (SM_CYBORDER), RGB(0,0,0));
  533.     hPrev = SelectObject (ps.hdc, hPen);
  534.     GetClientRect (hFrameWnd, &Rect);
  535.     Rect.top = Rect.bottom - EmacsCM.MLHeight;
  536.     MoveTo (ps.hdc, 0, Rect.top);
  537.     LineTo (ps.hdc, Rect.right, Rect.top);
  538.     SelectObject (ps.hdc, hPrev);
  539.     DeleteObject (hPen);
  540.  
  541.     EndPaint (hFrameWnd, &ps);
  542. } /* MLPaint */
  543.  
  544. /* spal:    set palette from $palette string */
  545. /* ====                                      */
  546.  
  547. PASCAL spal (char *pstr)
  548. {
  549. #if     COLOR
  550.     int     pal;    /* current palette position */
  551.  
  552.     for (pal = 0; pal < 16; pal++) {
  553.     DWORD   clr;    /* current color value */
  554.     int     i;
  555.         unsigned char n;
  556.  
  557.     if (*pstr== 0) break;
  558.     clr = 0;
  559.     for (i = 0; i < 3; i++) if (*pstr) {
  560.         n = *pstr++ - '0';
  561.         if (n >= 8) n = 255;
  562.         else n *= 32;
  563.         clr |= (DWORD)n << (i * 8);
  564.     }
  565.     EmacsPalette[pal] = clr;
  566.     }
  567. #endif
  568.     return 0;
  569. } /* spal */
  570.